﻿using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Transactions;
using System;

namespace ScrumFactory.Services.Logic {    

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]    
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    [Export(typeof(IProjectsService))]
    public class ProjectsService : IProjectsService
    {
        private Data.IProjectRepository projectsRepository;

        [ImportingConstructor()]
        public ProjectsService([Import] Data.IProjectRepository projectsRepository)
        {
            this.projectsRepository = projectsRepository;
        }

        [WebGet(UriTemplate = "Projects", ResponseFormat = WebMessageFormat.Json)]
        public ICollection<Project> GetAllProjects()
        {
            
            return this.projectsRepository.GetAllProjects();
            
        }

        [WebGet(UriTemplate = "Projects/?filter=Running", ResponseFormat = WebMessageFormat.Json)]
        public ICollection<Project> GetRunningProjects() {
            return this.projectsRepository.GetRunningProjects();
        }

        [WebGet(UriTemplate = "Projects/{projectUId}", ResponseFormat = WebMessageFormat.Json)]
        public Project GetProject(string projectUId)
        {
            Project p = projectsRepository.GetProject(projectUId);
            //p.Sprints = new List<Sprint>(projectsRepository.GetSprints(projectUId));
            return p;            
        }

        [WebInvoke(Method="PUT", UriTemplate = "Projects", ResponseFormat = WebMessageFormat.Json)]
        public void UpdateProject(Project project) {            
             projectsRepository.UpdateProject(project);            
        }

        [WebInvoke(Method = "POST", UriTemplate = "Projects", ResponseFormat = WebMessageFormat.Json)]
        public void CreateProject(Project project) {
            
            project.CreateDate = System.DateTime.Now;
            project.Status = (short)ProjectStatus.PROPOSAL_CREATION;
            projectsRepository.InsertProject(project);

            foreach (Role r in project.Roles)
                projectsRepository.SaveProjectRole(r);
            
        }

        [WebGet(UriTemplate = "Projects/{projectUId}/Roles", ResponseFormat = WebMessageFormat.Json)]
        public ICollection<Role> GetProjectRoles(string projectUId) {
            return projectsRepository.GetProjectRoles(projectUId);
        }

        [WebInvoke(Method = "POST", UriTemplate = "Projects/{projectUId}/Roles", ResponseFormat = WebMessageFormat.Json)]
        public void CreateProjectRole(string projectUId, Role role) {            
            projectsRepository.SaveProjectRole(role);
        }

        [WebInvoke(Method = "PUT", UriTemplate = "Projects/{projectUId}/Roles", ResponseFormat = WebMessageFormat.Json)]
        public void UpdateProjectRole(string projectUId, Role role) {            
            projectsRepository.SaveProjectRole(role);
        }

        [WebInvoke(Method = "DELETE", UriTemplate = "Projects/{projectUId}/Roles/{roleUId}", ResponseFormat = WebMessageFormat.Json)]
        public void DeleteProjectRole(string projectUId, string roleUId) {

            using (TransactionScope scope = new TransactionScope()) {
                try {

                    // make sure the role is not planned
                    if (!projectsRepository.CanDeleteProjectRole(roleUId))
                        throw new WebFaultException<String>("BRE_ROLE_ALREADY_PLANNED", System.Net.HttpStatusCode.BadRequest);

                    // delete it
                    projectsRepository.DeleteProjectRole(roleUId);

                    scope.Complete();
                } finally {
                    scope.Dispose();
                }
            }
                
        }

        [WebInvoke(Method = "POST", UriTemplate = "Projects/{projectUId}/Sprints", RequestFormat = WebMessageFormat.Json)]
        public void AddSprint(string projectUId, Sprint sprint) {
            projectsRepository.SaveSprint(sprint);
        }

        [WebInvoke(Method = "PUT", UriTemplate = "Projects/{projectUId}/Sprints", RequestFormat = WebMessageFormat.Json)]
        public ICollection<Sprint> UpdateSprint(string projectUId, Sprint sprint) {
            ICollection<Sprint> sprints = GetSprints(projectUId);
            PushSprintDates(sprint.SprintNumber, sprint.StartDate, sprint.EndDate, sprints);

            foreach (Sprint s in sprints)
                projectsRepository.SaveSprint(s);

            return sprints;
        }


        private void PushSprintDates(int sprintNumber, System.DateTime startDate, System.DateTime endDate, ICollection<Sprint> sprints) {

            // make sure sprints are ordered by number
            Sprint[] orderedSprint = sprints.OrderBy(s => s.SprintNumber).ToArray();

            // finds the sprint
            Sprint sprint = orderedSprint.SingleOrDefault(s => s.SprintNumber == sprintNumber);
            if (sprint == null)
                return;

            if (sprintNumber >= 2) {
                Sprint previous = orderedSprint[sprintNumber - 2];
                if (startDate <= previous.StartDate.AddDays(1))
                    throw new WebFaultException<String>("BRE_SPRINT_START_DATE_NOT_ALLOWED", System.Net.HttpStatusCode.BadRequest);                    
            }

            if (endDate < sprint.StartDate)
                throw new WebFaultException<String>("BRE_END_DATE_NOT_ALLOWED", System.Net.HttpStatusCode.BadRequest);

            // get the number of days to push the sprints
            int pushDays = 0;
            int startDatePushDays = (sprintNumber == 1) ? startDate.Subtract(sprint.StartDate).Days : 0;
            // if is the first sprint and the start date was changed, it override the end date change
            if (startDatePushDays != 0) {
                endDate = endDate.AddDays(startDatePushDays);
                pushDays = startDatePushDays;
            } else {
                pushDays = endDate.Subtract(sprint.EndDate).Days;
            }
            
            
            // update the sprint end date            
            sprint.EndDate = endDate;

            // pushs the following sprints, if this one is not the last
            if (sprintNumber < orderedSprint.Length && pushDays != 0) {
                for (int i = sprintNumber; i < orderedSprint.Length; i++) {
                    orderedSprint[i].StartDate = orderedSprint[i].StartDate.AddDays(pushDays);
                    orderedSprint[i].EndDate = orderedSprint[i].EndDate.AddDays(pushDays);
                }
            }
                                    
            // moves the previus sprint end date one day before this one start date
            if (sprintNumber > 1) 
                orderedSprint[sprintNumber - 2].EndDate = startDate.AddDays(-1);

            // update the sprint start date
            sprint.StartDate = startDate;
            

        }

        [WebGet(UriTemplate = "Projects/{projectUId}/Sprints", ResponseFormat = WebMessageFormat.Json)]
        public ICollection<Sprint> GetSprints(string projectUId) {
            return projectsRepository.GetSprints(projectUId).OrderBy(s => s.SprintNumber).ToList();
        }


    }
}
